﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using WPFCABShell.$safeprojectname$.Interface;
using WPFCABShell.$safeprojectname$.Interface.Services.Module;
using WPFCABShell.$safeprojectname$.Interface.Services.Options;
using WPFCABShell.$safeprojectname$.Services.Module;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.Configuration;
using Microsoft.Practices.ObjectBuilder;

namespace WPFCABShell.$safeprojectname$.Options.Plugins
{
	public sealed class PluginsPresenter : Presenter<IPluginsView>
	{
		private readonly WhitelistModuleEnumerator _moduleEnumerator;

		public PluginsPresenter([Dependency] WhitelistModuleEnumerator moduleEnumerator)
		{
			_moduleEnumerator = moduleEnumerator;
		}

		protected override void OnViewSet()
		{
			base.OnViewSet();

			//when the view loads, pass on all plugin infos to it
			View.Loaded += delegate
			{
				View.PluginInfos = GetPluginInfos();
			};
		}

		private ICollection<IPluginInfo> GetPluginInfos()
		{
			List<IPluginInfo> pluginInfos = new List<IPluginInfo>();

			foreach (IModuleInfo moduleInfo in _moduleEnumerator.AllModules)
			{
				PluginInfo pluginInfo = PluginInfo.FromModuleInfo(_moduleEnumerator, moduleInfo);
				pluginInfos.Add(pluginInfo);
			}

			return pluginInfos;
		}

		public void ApplyChanges()
		{
			foreach (PluginInfo pluginInfo in View.PluginInfos)
			{
				if (pluginInfo.IsWhitelisted)
				{
					_moduleEnumerator.AddToWhitelist(pluginInfo.ModuleInfo);
				}
				else
				{
					_moduleEnumerator.RemoveFromWhitelist(pluginInfo.ModuleInfo);
				}
			}
		}

		private sealed class PluginInfo : IPluginInfo, INotifyPropertyChanged
		{
			private readonly IModuleInfo _moduleInfo;
			private readonly string _path;
			private readonly string _name;
			private readonly string _description;
			private readonly Version _version;
			private readonly bool _isImplicitlyTrusted;
			private readonly ModuleStatus _moduleStatus;
			private bool _isWhitelisted;

			public IModuleInfo ModuleInfo
			{
				get
				{
					return _moduleInfo;
				}
			}

			public string Path
			{
				get
				{
					return _path;
				}
			}

			public string Name
			{
				get
				{
					return _name;
				}
			}

			public string Description
			{
				get
				{
					return _description;
				}
			}

			public Version Version
			{
				get
				{
					return _version;
				}
			}

			public bool IsImplicitlyTrusted
			{
				get
				{
					return _isImplicitlyTrusted;
				}
			}

			public bool IsWhitelisted
			{
				get
				{
					return _isWhitelisted;
				}
				set
				{
					if (value != _isWhitelisted)
					{
						_isWhitelisted = value;
						OnPropertyChanged("IsWhitelisted");
					}
				}
			}

			public ModuleStatus ModuleStatus
			{
				get
				{
					return _moduleStatus;
				}
			}

			public event PropertyChangedEventHandler PropertyChanged;

			private PluginInfo(IModuleInfo moduleInfo, bool isImplicitlyTrusted, bool isWhitelisted, string name, Version version, ModuleStatus moduleStatus, string path, string description)
			{
				_moduleInfo = moduleInfo;
				_isImplicitlyTrusted = isImplicitlyTrusted;
				_isWhitelisted = isWhitelisted;
				_name = name;
				_version = version;
				_moduleStatus = moduleStatus;
				_path = path;
				_description = description;
			}

			internal static PluginInfo FromModuleInfo(WhitelistModuleEnumerator moduleEnumerator, IModuleInfo moduleInfo)
			{
				ModuleTrustStatus moduleTrustStatus = moduleEnumerator.GetModuleTrustStatus(moduleInfo);
				Assembly assembly = moduleEnumerator.GetAssemblyForModule(moduleInfo);
				string name = assembly.GetName().Name;
				IList<CustomAttributeData> customAttributes = CustomAttributeData.GetCustomAttributes(assembly);
				CustomAttributeData moduleAttribute = GetCustomAttributeForType(customAttributes, typeof(ModuleAttribute));

				if (moduleAttribute != null)
				{
					name = moduleAttribute.ConstructorArguments[0].Value as string;
				}

				Version version = assembly.GetName().Version;
				ModuleStatus status = moduleEnumerator.GetStatus(moduleInfo);
				string path = assembly.CodeBase;
				string description = null;

				CustomAttributeData assemblyDescriptionAttribute = GetCustomAttributeForType(customAttributes, typeof(AssemblyDescriptionAttribute));

				if (assemblyDescriptionAttribute != null)
				{
					description = assemblyDescriptionAttribute.ConstructorArguments[0].Value as string;
				}

				return new PluginInfo(moduleInfo, moduleTrustStatus == ModuleTrustStatus.ImplicitlyTrusted, moduleTrustStatus == ModuleTrustStatus.Whitelisted, name, version, status, path, description);
			}

			private static CustomAttributeData GetCustomAttributeForType(IList<CustomAttributeData> customAttributes, Type type)
			{
				foreach (CustomAttributeData customAttribute in customAttributes)
				{
					if (string.Equals(customAttribute.Constructor.DeclaringType.FullName, type.FullName, StringComparison.Ordinal))
					{
						return customAttribute;
					}
				}

				return null;
			}

			private void OnPropertyChanged(string propertyName)
			{
				PropertyChangedEventHandler handler = PropertyChanged;

				if (handler != null)
				{
					handler(this, new PropertyChangedEventArgs(propertyName));
				}
			}
		}
	}
}
